﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BaiTu.Framework;
using System.Data.SqlClient;

namespace BaiTu.DAL.EF
{
    /// <summary>
    /// 通过EF实现数据操作的工具类
    /// </summary>
    public class Repository<T> : IDAL.IBaseRepository<T>, IDisposable
        where T : Domains.BaseDomain, new()
    {
        protected readonly IDBContext _context;
        private IDbSet<T> _entities;

        public Repository(IDBContext context)
        {
            this._context = context;
        }

        /// <summary>
        /// 通过主键获取对象数据
        /// </summary>
        /// <param name="id">Identifier</param>
        /// <returns>Entity</returns>
        public virtual T GetById(string id)
        {
            //see some suggested performance optimization (not tested)
            //http://stackoverflow.com/questions/11686225/dbset-find-method-ridiculously-slow-compared-to-singleordefault-on-id/11688189#comment34876113_11688189
            return this.Entities.Find(id);
        }

        /// <summary>
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// </summary>
        public virtual IQueryable<T> TableNoTracking
        {
            get
            {
                return this.Entities.AsNoTracking();
            }
        }
        /// <summary>
        /// Entities
        /// </summary>
        protected virtual IDbSet<T> Entities
        {
            get
            {
                if (_entities == null)
                    _entities = _context.Set<T>();
                return _entities;
            }
        }

        /// <summary>
        /// Gets a table
        /// </summary>
        public virtual IQueryable<T> Table
        {
            get
            {
                return this.Entities;
            }
        }

        /// <summary>
        /// 添加一个列表集合
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public BaiTu.Framework.InnerResult Add(IEnumerable<T> list, bool useTransaction = false)
        {
            BaiTu.Framework.InnerResult innerResult = new Framework.InnerResult();
            try
            {
                if (list == null)
                {
                    Exception ex = new ArgumentNullException("list");
                    innerResult = ex.GetInnerResult();
                }
                else
                {
                    IsCommited = false;
                    this._context.CurrentContext.Configuration.AutoDetectChangesEnabled = false;

                    foreach (var item in list)
                    {
                        this._context.EntryEntity<T>(item).State = EntityState.Added;
                        //this.Entities.Add(item);
                    }
                    innerResult.Success = true;
                    if (useTransaction == false)
                    {
                        innerResult = Commit();
                    }

                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
                    }

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                innerResult = fail.GetInnerResult();
            }
            finally
            {
                this._context.CurrentContext.Configuration.AutoDetectChangesEnabled = true;
            }
            return innerResult;
        }
        public BaiTu.Framework.InnerResult Add(T entity, bool useTransaction = false)
        {
            BaiTu.Framework.InnerResult innerResult = new Framework.InnerResult();
            try
            {
                if (entity == null)
                {
                    Exception ex = new ArgumentNullException("entity");
                    innerResult = ex.GetInnerResult();
                }
                else
                {
                   
                    //this.Entities.Add(entity);
                    this._context.EntryEntity<T>(entity).State = EntityState.Added;

                    IsCommited = false;
                    if (useTransaction == false)
                    {
                        innerResult = Commit();
                        
                    }
                    else
                    {
                        innerResult.Success = true;
                    }
                    innerResult.ID = entity.ID;
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                innerResult = fail.GetInnerResult();
            }
            return innerResult;
        }
        /// <summary>
        /// 删除多条相同类型的数据
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public BaiTu.Framework.InnerResult Delete(IEnumerable<T> list,bool useTransaction=false)
        {
            BaiTu.Framework.InnerResult innerResult = new Framework.InnerResult();
            try
            {
                if (list == null)
                {
                    Exception ex = new ArgumentNullException("list");
                    innerResult = ex.GetInnerResult();
                }
                else
                {
                    IsCommited = false;
                    ///在进行数据的变更时  EF默认会自动的跟踪数据的变化。当变更的数据库较大的时候，EF的跟踪工作量纠会骤增，使指定操作变得非常缓慢。
                    ///只要在批量操作的时候把自动跟踪关闭 
                    this._context.CurrentContext.Configuration.AutoDetectChangesEnabled = false;
                    foreach (var item in list)
                    {
                        this._context.EntryEntity<T>(item).State = EntityState.Deleted;
                        ///不能直接采用remove 因为要删除remove  必须保证entity 已经附加到entities中
                        //this.Entities.Remove(item);
                    }
                    if (useTransaction)
                    {
                        innerResult.Success = true;
                    }
                    else
                    {
                        innerResult = Commit();
                    }
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                innerResult = fail.GetInnerResult();
            }
            finally
            {
                ///执行完毕之后 启用自动状态更新
                this._context.CurrentContext.Configuration.AutoDetectChangesEnabled = true;
            }
            return innerResult;
        }
        public BaiTu.Framework.InnerResult Delete(T entity,bool useTransaction=false)
        {
            BaiTu.Framework.InnerResult innerResult = new Framework.InnerResult();
            try
            {
                if (entity == null)
                {
                    Exception ex = new ArgumentNullException("entity");
                    innerResult = ex.GetInnerResult();
                }
                else
                {
                    
                    this._context.EntryEntity<T>(entity).State = EntityState.Deleted;
                    IsCommited = false;
                    //this.Entities.Remove(entity);
                    if (useTransaction)
                    {
                        innerResult.Success = true;
                    }
                    else
                    {
                        innerResult = Commit();
                    }
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                innerResult = fail.GetInnerResult();
            }
            return innerResult;
        }
        /// <summary>
        /// 通过lambda 表达式来进行数据的删除
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public BaiTu.Framework.InnerResult Delete(Func<T, bool> predicate,bool useTransaction=false)
        {
            BaiTu.Framework.InnerResult innerResult = new InnerResult();
            IEnumerable<T> listEntity = Query(predicate);
            if (listEntity == null || listEntity.Count() == 0)
            {
                innerResult.Success = true;
                return innerResult;
            }

            return Delete(listEntity,useTransaction);
        }

        /// <summary>
        /// 通过lambda 表达式进行查询
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public IEnumerable<T> Query(Func<T, bool> predicate)
        {
            return this.TableNoTracking.Where(predicate);
        }

        public BaiTu.Framework.InnerResult Update(IEnumerable<T> list,bool useTransaction=false)
        {
            BaiTu.Framework.InnerResult innerResult = new Framework.InnerResult();
            try
            {
                if (list == null)
                {
                    Exception ex = new ArgumentNullException("list");
                    innerResult = ex.GetInnerResult();
                }
                else
                {
                    this._context.CurrentContext.Configuration.AutoDetectChangesEnabled = false;

                    foreach (var item in list)
                    {
                        this._context.EntryEntity<T>(item).State = EntityState.Modified;
                    }
                    if (useTransaction == true)
                    {
                        innerResult.Success = true;
                    }
                    else
                    {
                        innerResult = Commit();
                    }
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                innerResult = fail.GetInnerResult();
            }
            finally
            {
                this._context.CurrentContext.Configuration.AutoDetectChangesEnabled = true;
            }
            return innerResult;
        }
        /// <summary>
        /// 更新采用先删除后添加的方式进行
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public BaiTu.Framework.InnerResult Update(T entity,bool useTransaction=false)
        {
            BaiTu.Framework.InnerResult innerResult = new Framework.InnerResult();
            try
            {
                if (entity == null)
                {
                    Exception ex = new ArgumentNullException("entity");
                    innerResult = ex.GetInnerResult();
                }
                else
                {
                    this._context.EntryEntity<T>(entity).State = EntityState.Modified;
                    IsCommited = false;
                    if (useTransaction == true)
                    {
                        innerResult.Success = true;
                    }
                    else
                    {                        
                        innerResult = Commit();
                    }                                        
                    innerResult.ID = entity.ID;
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                innerResult = fail.GetInnerResult();
            }
            return innerResult;
        }

        /// <summary>
        /// 执行sql命令
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="DonNotUseTranstraction"></param>
        /// <param name="timeOut"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public int ExecuteSqlCommand(string sql, bool DonNotUseTranstraction = false, int? timeOut = null, params object[] args)
        {
            int result = _context.ExecuteSqlCommand(sql, DonNotUseTranstraction, timeOut, args);
            return result;
        }
        /// <summary>
        /// 执行sql 查询
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
        {
            return _context.SqlQuery<TElement>(sql, parameters);
        }
        /// <summary>
        /// 执行存储过程
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="commandText"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
             where TEntity : Domains.BaseDomain, new()
        {
            return _context.ExecuteStoredProcedureList<TEntity>(commandText, parameters);
        }
        /// <summary>
        /// 是否提交操作
        /// </summary>
        public bool IsCommited
        {
            get;
            private set;
        }

        public InnerResult Commit()
        {
            Framework.InnerResult innerResult = new InnerResult();
            if (IsCommited)
            {
                innerResult.Success = true;
                innerResult.ErrorMessage = "数据已经提交，无需重复提交";
                return innerResult;
            }
            try
            {
                int result = this._context.SaveChanges();
                IsCommited = true;
                innerResult.Success = true;
            }
            catch (DbUpdateException ex)
            {
                if (ex.InnerException != null && ex.InnerException.InnerException is SqlException)
                {
                    SqlException sqlEx = ex.InnerException.InnerException as SqlException;
                    innerResult = ex.GetInnerResult();
                }
            }
            return innerResult;
        }
        /// <summary>
        /// 把当前单元操作回滚成未提交状态
        /// </summary>
        /// <returns></returns>
        public InnerResult RollBack()
        {
            IsCommited = false;
            InnerResult innerResult = new InnerResult();
            innerResult.Success = true;
            return innerResult;
        }

        public void Dispose()
        {
            if (!IsCommited)
            {
                Commit();
            }
            this._context.CurrentContext.Dispose();
        }
    }
}

